#include <stdio.h>
#include <winsock2.h>
#include <vector>
#include "hse_common.h"
#include "hse_types.h"
#include "document_meta_datas.h"
#include "search.h"
#include "search_engine.h"
#include "win_server.h"
#include "time.h"

namespace hse {

static const char *kHomePage = 
		"HTTP/1.1 200 OK\r\nContent-type:text/html;\r\n\r\n\
		<html><head><title>HSE search</title></head>\
		<body><form action=''><center><input name='q' type='text' /><input type='submit' value='search' />\
		</center></form></body></html>";
static const char *kResultPageHeader = 
		"HTTP/1.1 200 OK\r\nContent-type:text/html;\r\n\r\n\<html><head><title>HSE search</title><style type='text/css'>\
		em {color:red};</style></head>\
		<body><form action=''><center><input name='q' type='text' value='";
static const char *kResultPageMiddle = 
		"' /><input type='submit' value='search' /></center></form>";
static const char *kABegin = 
		"<a target='_blank' href='?docid=";
static const char *kViewFileHeader = 
		"HTTP/1.1 200 OK\r\nContent-type:text/plain;\r\n\r\n";
static const char *kFileNotFound =
		"HTTP/1.1 404 Not Found\r\nContent-type:text/plain;\r\n\r\nFile Not Found";

static const size_t kHomePageLen = strlen(kHomePage);
static const size_t kResultPageHeaderLen = strlen(kResultPageHeader);
static const size_t kResultPageMiddleLen = strlen(kResultPageMiddle);
static const size_t kABeginLen = strlen(kABegin);
static const size_t kViewFileHeaderLen = strlen(kViewFileHeader);
static const size_t kFileNotFoundLen = strlen(kFileNotFound);


void WinServer::Start(u_short port) {
	WSAData wsaData;
	WSAStartup(MAKEWORD(2,2),&wsaData);
	sockaddr_in addr;
	addr.sin_port = htons(port);
	addr.sin_addr.S_un.S_addr = INADDR_ANY;
	addr.sin_family = AF_INET;
	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == INVALID_SOCKET) {
		LOG_ERROR(fprintf(stderr, "Can not create socket, error code=%d!\n", WSAGetLastError()));
		exit(-1);
	}
	if (bind(sock, (sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
		LOG_ERROR(fprintf(stderr, "Can not bind socket, address may be in use, error code=%d!\n", WSAGetLastError()));
		exit(-1);
	}
	listen(sock, 5);
	search_engine_ = new SearchEngine(string("D:\\index\\"));
	while (true) {
		sockaddr_in caddr;
		int l = sizeof(caddr);
		SOCKET csock = accept(sock, (sockaddr *)&caddr, &l);
		HandleRequest(csock, caddr);
	}
}

void WinServer::Send404(SOCKET sock) {
	send(sock, kFileNotFound, kFileNotFoundLen, 0);
	shutdown(sock, 2);
}

void WinServer::HandleViewDoc(SOCKET sock, int doc_id) {
	DocumentMetaData* meta = hse::DocumentMetaDatas::Instance().GetMetaData(doc_id);
	if (meta == NULL)
		return Send404(sock);
	string& full_name = meta->full_name;
	FILE *f = fopen(full_name.c_str(), "r");
	if (f == NULL)
		return Send404(sock);
	send(sock, kViewFileHeader, kViewFileHeaderLen, 0);
	char buffer[4096];
	int l;
	while ((l = fread(buffer, 1, 4096, f)) > 0) {
		send(sock, buffer, l, 0);
	}
	fclose(f);
	shutdown(sock, 2);
}

void WinServer::HandleRequest(SOCKET sock, sockaddr_in addr) {
	char *buf = new char[512];
	char *query = new char[512];
	//GET / HTTP/1.1
	//GET /?q=*** HTTP/1.1
	//GET /?docid=*** HTTP/1.1
	int p = 0, q = 0, l = 0;
	bool space_occured = false;
	bool equal_occured = false;
	bool is_query = true;
	l = recv(sock, buf, 512 - 1, 0);
	buf[l] = '\0';
	for (int i = 3; i < l; ++i) {
		if(buf[i] == ' ') {
			if (space_occured) {
				query[q] = '\0';
				if (is_query) {
					HandleSearch(sock, query);
				} else {
					HandleViewDoc(sock, atoi(query));
				}
				break;
			}
			else {
				space_occured = true;
				if (buf[i + 2] == ' ') {
					HandleHome(sock);
					break;
				} else if(buf[i + 3] == 'q'){
					i += 4;
				} else if(buf[i + 8] == '=') {
					i += 8;
					is_query = false;
				} else {
					Send404(sock);
					break;
				}
				
				if (buf[i] != '=') {
					LOG_INFO(fprintf(stdout, "Bad request:%s\n", buf));
					Send404(sock);
					break;
				}
			}
		} else if (space_occured) {
			query[q++] = buf[i] != '+' ? buf[i] : ' ';
		}
	}
	delete buf;
	delete query;
}

void WinServer::HandleHome(SOCKET sock) {
	LOG_INFO(printf("return home page\n"));
	send(sock, kHomePage, kHomePageLen, 0);
	shutdown(sock, 2);
}

void WinServer::HandleSearch(SOCKET s, const char *query) {
	LOG_INFO(printf("handle query:%s\n", query));
	std::vector<SearchResult> result;
	clock_t t1 = clock();
	int total;
	int query_time = search_engine_->RunSearch(string(query), 0, 10, result, &total, 150);
	long t = clock() - t1;
	char speed[255];
	char doc_id[20];
	int doc_id_len;
	int speed_s_len = sprintf(speed, "<p>use %d millseconds to search %d results(query time:%d).</p>", t, total, query_time);

	send(s, kResultPageHeader, kResultPageHeaderLen, 0);
	send(s, query, strlen(query), 0);
	send(s, kResultPageMiddle, kResultPageMiddleLen, 0);
	send(s, speed, speed_s_len, 0);
	send(s, "<table>", 7, 0);
	for(size_t i = 0; i < result.size(); ++i) {
		send(s, "<tr><td><b>", 11, 0);
		send(s, kABegin, kABeginLen, 0); //<a target='_blank' href='?docid=
		doc_id_len = sprintf(doc_id, "%d", result[i].doc_id);
		send(s, doc_id, doc_id_len, 0);
		send(s, "'>", 2, 0);
		send(s, result[i].doc_name.c_str(), result[i].doc_name.size(), 0);
		send(s, "</a></b>", 8, 0);
		send(s, "</td></tr><tr><td>", 18, 0);
		send(s, result[i].segment.c_str(), result[i].segment.size(), 0);
		send(s, "</td></tr>", 10, 0);
	}
	send(s, "</table>", 8, 0);
	send(s, "</body></html>", 14, 0);
	shutdown(s, 2);
}


}